﻿using System.Collections;
using System.Globalization;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ExampleService.Host
{
    public class VolcanoEngineV2
    {
        public string Host { get; set; }
        public string URL { get; set; }
        public string APIKey { get; set; }
        public string APISecret { get; set; }

        private const string Service = "translate";
        private const string Version = "1.0.16";
        private const string Region = "cn-north-1";
        private const string UrlQuery = "Action=TranslateText&Version=2020-06-01";
        private const string Algorithm = "HMAC-SHA256";

        //private readonly List<string> _includeHeaders;
        private List<string> _includeHeaders;
        private readonly HttpClient _httpClient;

        private string _nowDate;
        private string _nowTime;
        private string _dateTimeSignStr;
        private string _bodyHash;

        public VolcanoEngineV2()
        {
            InitializeProperties();
            _httpClient = CreateHttpClient();
        }

        public VolcanoEngineV2(string apiKey, string apiSecret)
        {
            APIKey = apiKey;
            APISecret = apiSecret;
            InitializeProperties();
            _httpClient = CreateHttpClient();
        }

        private void InitializeProperties()
        {
            Host = "translate.volcengineapi.com";
            URL = $"https://{Host}/?{UrlQuery}";

            _includeHeaders = new List<string>
            {
                "Content-Type",
                "Content-Md5",
                "Host"
            };
        }

        private static HttpClient CreateHttpClient()
        {
            var handler = new SocketsHttpHandler
            {
                PooledConnectionLifetime = TimeSpan.FromMinutes(15),
                UseProxy = false
            };

            return new HttpClient(handler)
            {
                Timeout = TimeSpan.FromSeconds(30)
            };
        }

        public async Task<string> TranslateTextAsync(string text, string sourceLanguage, string targetLanguage, CancellationToken cancellationToken = default)
        {
            var requestBody = BuildRequestJson(text, sourceLanguage, targetLanguage);

            var dateTimeSign = DateTime.UtcNow;
            _nowDate = dateTimeSign.ToString("yyyyMMdd");
            _nowTime = dateTimeSign.ToString("HHmmss");
            _dateTimeSignStr = $"{_nowDate}T{_nowTime}Z";

            var translated = await SendRequestAsync(requestBody, cancellationToken);
            var translations = ParseResponseJson(translated);

            return string.Join('\n', translations);
        }

        private static string ComputeHash256(string input)
        {
            using var sha256 = SHA256.Create();
            var inputBytes = Encoding.UTF8.GetBytes(input);
            var hashedBytes = sha256.ComputeHash(inputBytes);
            return Convert.ToHexString(hashedBytes).ToLower();
        }

        private static byte[] HmacSha256(byte[] data, byte[] key)
        {
            using var hmac = new HMACSHA256(key);
            return hmac.ComputeHash(data);
        }

        private static byte[] HmacSha256(string text, byte[] key)
        {
            return HmacSha256(Encoding.UTF8.GetBytes(text), key);
        }

        private string BuildRequestJson(string text, string sourceLanguage, string targetLanguage)
        {
            var volcanoRequest = new VolcanoRequestV2
            {
                SourceLanguage = sourceLanguage,
                TargetLanguage = targetLanguage,
                TextList = new[] { text }
            };

            return JsonSerializer.Serialize(volcanoRequest, new JsonSerializerOptions
            {
                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
            });
        }

        private string[] ParseResponseJson(string jsonString)
        {
            using var document = JsonDocument.Parse(jsonString);
            var translationList = document.RootElement.GetProperty("TranslationList");

            var translations = new string[translationList.GetArrayLength()];
            for (var i = 0; i < translations.Length; i++)
            {
                translations[i] = translationList[i].GetProperty("Translation").GetString() ?? string.Empty;
            }

            return translations;
        }

        private async Task<string> SendRequestAsync(string requestBody, CancellationToken cancellationToken)
        {
            var byteArray = Encoding.UTF8.GetBytes(requestBody);
            _bodyHash = ComputeHash256(requestBody);

            using var request = new HttpRequestMessage(HttpMethod.Post, URL)
            {
                Content = new ByteArrayContent(byteArray)
                {
                    Headers =
                    {
                        ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")
                    }
                }
            };

            //using var request = new HttpRequestMessage(HttpMethod.Post, URL);
            //var content = new StringContent(requestBody, Encoding.UTF8, "application/json"); 

            request.Headers.Add("X-Date", _dateTimeSignStr);
            request.Headers.Add("X-Content-Sha256", _bodyHash);
            request.Headers.Add("Host", Host);
            request.Headers.UserAgent.ParseAdd($"volc-sdk-dotnet/v{Version}");

            var signedHeaders = GetSignedHeaders(request);
            var authorizationHeader = BuildAuthorizationHeader(requestBody, signedHeaders);
            request.Headers.TryAddWithoutValidation("Authorization", authorizationHeader);

            try
            {
                using var response = await _httpClient.SendAsync(request, cancellationToken);
                response.EnsureSuccessStatusCode();

                return await response.Content.ReadAsStringAsync(cancellationToken);
            }
            catch (HttpRequestException ex)
            {
                throw new InvalidOperationException($"API request failed: {ex.Message}", ex);
            }
        }

        private List<string> GetSignedHeaders(HttpRequestMessage request)
        {
            var signedHeaders = new List<string>();

            foreach (var header in request.Headers)
            {
                if (_includeHeaders.Contains(header.Key) || header.Key.StartsWith("X-"))
                {
                    signedHeaders.Add(header.Key.ToLower());
                }
            }

            //signedHeaders.Add("host");
            signedHeaders.Sort();

            return signedHeaders;
        }

        private string BuildAuthorizationHeader(string requestBody, List<string> signedHeaders)
        {
            signedHeaders.Add("content-type");
            signedHeaders.Sort();

            var signedHeadersStr = string.Join(';', signedHeaders);
            var signedHeadersToSignStr = BuildSignedHeadersString(signedHeaders);

            var canonicalRequest = string.Join('\n',
                "POST",
                "/",
                UrlQuery,
                signedHeadersToSignStr,
                signedHeadersStr,
                ComputeHash256(requestBody));

            var hashedCanonReq = ComputeHash256(canonicalRequest);

            var stringToSign = string.Join('\n',
                Algorithm,
                _dateTimeSignStr,
                $"{_nowDate}/{Region}/{Service}/request",
                hashedCanonReq);

            var signingKey = DeriveSigningKey();
            var signatureBytes = HmacSha256(stringToSign, signingKey);
            var signature = Convert.ToHexString(signatureBytes).ToLower();

            return $"{Algorithm} Credential={APIKey}/{_nowDate}/{Region}/{Service}/request, " +
                   $"SignedHeaders={signedHeadersStr}, Signature={signature}";
        }

        private string BuildSignedHeadersString(List<string> signedHeaders)
        {
            var builder = new StringBuilder();

            foreach (var header in signedHeaders)
            {
                var headerValue = string.Empty;
                switch (header)
                {
                    case "host":
                        headerValue = Host;
                        break;
                    case "content-type":
                        headerValue = "application/json";
                        break;
                    case "x-date":
                        headerValue = _dateTimeSignStr;
                        break;
                    case "x-content-sha256":
                        headerValue = _bodyHash;
                        break;
                    default:
                        headerValue = string.Join(", ", _httpClient.DefaultRequestHeaders.GetValues(header));
                        break;
                }
                //var headerValue = header == "content-type"
                //    ? "application/json"
                //    : string.Join(", ", _httpClient.DefaultRequestHeaders.GetValues(header));

                builder.Append(header).Append(':').Append(headerValue).Append('\n');
            }

            return builder.ToString();
        }

        private byte[] DeriveSigningKey()
        {
            var kDate = HmacSha256(_nowDate, Encoding.UTF8.GetBytes(APISecret));
            var kRegion = HmacSha256(Region, kDate);
            var kService = HmacSha256(Service, kRegion);
            return HmacSha256("request", kService);
        }

        public void Dispose()
        {
            _httpClient?.Dispose();
        }
    }

    public class VolcanoRequestV2
    {
        [JsonPropertyName("SourceLanguage")]
        public string SourceLanguage { get; set; } = string.Empty;

        [JsonPropertyName("TargetLanguage")]
        public string TargetLanguage { get; set; } = string.Empty;

        [JsonPropertyName("TextList")]
        public string[] TextList { get; set; } = Array.Empty<string>();
    }
}